using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Text.RegularExpressions;
using Destra.Tools.DACGenerator.Config;

namespace Destra.Tools.DACGenerator.CodeGenerators
{
    public class EntityCodeGenerator : CodeDomGenerator
    {
        public EntityCodeGenerator(CodeDomProvider codeDomProvider)
            :
            base(codeDomProvider)
        {

        }

        public string Generate(List<QueryInfo.OutputColumn> outputColumns, EntityGeneratorConfig entityGeneratorConfig)
        {
            Regex argValueRegex =
                new Regex(
                    @"(?:\s*)
                                        (?<argument>(?:""[^""]*"")|(?:[^,\)=]+))+  (?# optionally quoted argument)
                                        (?:\s*)?
                                        (?:=\s*(?<value>(?:""[^""]*"")|(?:[^,\)]+)))? (?# optional, optionally quoted, value)
                                        (?:\s*)?
                                        ",
                    RegexOptions.IgnorePatternWhitespace);

            CodeNamespace codeNamespace = new CodeNamespace(entityGeneratorConfig.ClassConfig.Namespace);

            CodeTypeDeclaration codeTypeDeclaration = new CodeTypeDeclaration();
            codeTypeDeclaration.Attributes = MemberAttributes.Public;
            codeTypeDeclaration.IsPartial = true;
            codeTypeDeclaration.Name = entityGeneratorConfig.ClassConfig.Class;

            // add the interfaces (if there are any)
            if (!string.IsNullOrEmpty(entityGeneratorConfig.EntityInterfaces))
            {
                foreach (string interfaceName in entityGeneratorConfig.EntityInterfaces.Split(",".ToCharArray()))
                {
                    codeTypeDeclaration.BaseTypes.Add(interfaceName);
                }
            }

            if (!string.IsNullOrEmpty(entityGeneratorConfig.EntityCustomAttributes))
            {
                foreach (string attOrig in entityGeneratorConfig.EntityCustomAttributes.Split(";".ToCharArray()))
                {
                    string att = attOrig.Trim("[]".ToCharArray());
                    CodeAttributeDeclaration attribute = new CodeAttributeDeclaration();
                    if (!att.Contains("("))
                    {
                        attribute.Name = att;
                    }
                    else
                    {
                        attribute.Name = att.Substring(0, att.IndexOf("("));

                        int startIndex = att.IndexOf("(") + 1;
                        int length = att.LastIndexOf(")") - startIndex;
                        string argumentsString = att.Substring(startIndex, length);

                        MatchCollection matches = argValueRegex.Matches(argumentsString);

                        foreach (Match match in matches)
                        {
                            CodeAttributeArgument arg = new CodeAttributeArgument();
                            if (!string.IsNullOrEmpty(match.Groups["value"].Value))
                            {
                                arg.Name = match.Groups["argument"].Value;
                                arg.Value = new CodeSnippetExpression(match.Groups["value"].Value);
                            }
                            else
                            {
                                arg.Value = new CodeSnippetExpression(match.Groups["argument"].Value);
                            }
                            attribute.Arguments.Add(arg);
                        }
                    }
                    codeTypeDeclaration.CustomAttributes.Add(attribute);
                }
            }

            codeNamespace.Types.Add(codeTypeDeclaration);

            if (!string.IsNullOrEmpty(entityGeneratorConfig.EntityUsingStatements))
            {
                foreach (string usingStatement in entityGeneratorConfig.EntityUsingStatements.Split(",".ToCharArray()))
                {
                    codeNamespace.Imports.Add(new CodeNamespaceImport(usingStatement));
                }
            }

            foreach (QueryInfo.OutputColumn outputColumn in outputColumns)
            {
                CodeMemberField field = CodeDomHelper.AddFieldToClass(
                    codeTypeDeclaration,
                    MemberAttributes.Private,
                    "m_" + outputColumn.Name,
                    outputColumn.Type,
                    outputColumn.Nullable);


                CodeMemberProperty property = CodeDomHelper.AddSimplePropertyToClass(
                    codeTypeDeclaration,
                    MemberAttributes.Public | MemberAttributes.Final,
                    outputColumn.Name,
                    outputColumn.Type,
                    "m_" + outputColumn.Name,
                    outputColumn.Nullable);

                foreach (EntityGeneratorConfig.CustomEntityAttribute att in entityGeneratorConfig.CustomAttributes)
                {
                    if (att.PropertyName.Equals(outputColumn.Name))
                    {
                        CodeAttributeDeclaration attribute = new CodeAttributeDeclaration();
                        if (!att.PropertyAttribute.Contains("("))
                        {
                            attribute.Name = att.PropertyAttribute;
                        }
                        else
                        {
                            attribute.Name = att.PropertyAttribute.Substring(0, att.PropertyAttribute.IndexOf("("));

                            int startIndex = att.PropertyAttribute.IndexOf("(") + 1;
                            int length = att.PropertyAttribute.LastIndexOf(")") - startIndex;
                            string argumentsString = att.PropertyAttribute.Substring(startIndex, length);

                            MatchCollection matches = argValueRegex.Matches(argumentsString);

                            foreach (Match match in matches)
                            {
                                CodeAttributeArgument arg = new CodeAttributeArgument();
                                if (!string.IsNullOrEmpty(match.Groups["value"].Value))
                                {
                                    arg.Name = match.Groups["argument"].Value;
                                    arg.Value = new CodeSnippetExpression(match.Groups["value"].Value);
                                }
                                else
                                {
                                    arg.Value = new CodeSnippetExpression(match.Groups["argument"].Value);
                                }
                                attribute.Arguments.Add(arg);
                            }
                        }
                        attribute.Name = attribute.Name.Trim("[]".ToCharArray());
                        property.CustomAttributes.Add(attribute);
                    }
                }


            }

            return GenerateCodeFromNamespace(codeNamespace);
        }
    }
}
